//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.quickserver.net.server.impl;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import net.sf.json.JSONObject;
import org.quickserver.net.AppException;
import org.quickserver.net.model.*;
import org.quickserver.net.qsadmin.QSAdminShell;
import org.quickserver.net.server.AuthStatus;
import org.quickserver.net.server.Authenticator;
import org.quickserver.net.server.ClientAuthenticationHandler;
import org.quickserver.net.server.ClientBinaryHandler;
import org.quickserver.net.server.ClientCommandHandler;
import org.quickserver.net.server.ClientData;
import org.quickserver.net.server.ClientEvent;
import org.quickserver.net.server.ClientEventHandler;
import org.quickserver.net.server.ClientExtendedEventHandler;
import org.quickserver.net.server.ClientHandler;
import org.quickserver.net.server.ClientIdentifiable;
import org.quickserver.net.server.ClientObjectHandler;
import org.quickserver.net.server.ClientWriteHandler;
import org.quickserver.net.server.DataMode;
import org.quickserver.net.server.DataType;
import org.quickserver.net.server.QuickServer;
import org.quickserver.net.server.TheClient;
import org.quickserver.util.Assertion;
import org.quickserver.util.MyString;

public abstract class BasicClientHandler implements ClientHandler {
    private static final Logger logger = Logger.getLogger(BasicClientHandler.class.getName());
    protected static final String NEW_LINE = QuickServer.getNewLine();
    protected static final byte[] NEW_LINE_BYTES;
    protected Socket socket;
    protected volatile boolean authorised;
    protected int counAuthTry;
    protected int maxAuthTry;
    protected String timeoutMsg;
    protected String maxAuthTryMsg;
    protected int socketTimeout;
    protected volatile boolean connection;
    protected boolean lost;
    protected QuickServer quickServer;
    protected Authenticator authenticator;
    protected ClientAuthenticationHandler clientAuthenticationHandler;
    protected ClientEventHandler clientEventHandler;
    protected ClientExtendedEventHandler clientExtendedEventHandler;
    protected ClientCommandHandler clientCommandHandler;
    protected ClientObjectHandler clientObjectHandler;
    protected ClientBinaryHandler clientBinaryHandler;
    protected ClientData clientData;
    protected InputStream in;
    protected OutputStream out;
    protected BufferedReader bufferedReader;
    protected ObjectOutputStream o_out;
    protected ObjectInputStream o_in;
    protected BufferedInputStream b_in;
    protected BufferedOutputStream b_out;
    protected Logger appLogger;
    protected DataMode dataModeIN;
    protected DataMode dataModeOUT;
    protected boolean communicationLogging;
    protected Date clientConnectedTime;
    protected Date lastCommunicationTime;
    protected boolean secure;
    protected static final ThreadLocal threadEvent;
    protected String maxConnectionMsg;
    protected final Set clientEvents;
    protected ConcurrentLinkedQueue unprocessedClientEvents;
    protected volatile boolean closeOrLostNotified;
    protected final Object lockObj;
    protected volatile boolean willClean;
    protected String charset;
    private static Map idMap;
    private int instanceCount;
    private int id;
    private String name;
    private String hostAddress;
    private int port;
    protected SSLEngine sslEngine;
    protected int totalReadBytes;
    protected int totalWrittenBytes;

    static {
        NEW_LINE_BYTES = NEW_LINE.getBytes();
        threadEvent = new ThreadLocal();
        idMap = new HashMap();
    }

    private static int getNewId(int instanceCount) {
        BasicClientHandler.InstanceId instanceId = (BasicClientHandler.InstanceId)idMap.get("" + instanceCount);
        if (instanceId == null) {
            instanceId = new BasicClientHandler.InstanceId();
            idMap.put("" + instanceCount, instanceId);
        }

        return instanceId.getNextId();
    }

    public BasicClientHandler(int instanceCount) {
        this.maxAuthTry = 5;
        this.dataModeIN = null;
        this.dataModeOUT = null;
        this.communicationLogging = true;
        this.clientConnectedTime = null;
        this.lastCommunicationTime = null;
        this.secure = false;
        this.clientEvents = new HashSet();
        this.unprocessedClientEvents = new ConcurrentLinkedQueue();
        this.lockObj = new Object();
        this.instanceCount = instanceCount;
        this.id = getNewId(instanceCount);
        StringBuilder sb = new StringBuilder();
        sb.append("<ClientHandler-Pool#");
        sb.append(instanceCount);
        sb.append("-ID:");
        sb.append(this.id);
        sb.append(">");
        this.name = sb.toString();
    }

    public int getInstanceCount() {
        return this.instanceCount;
    }

    public BasicClientHandler() {
        this(-1);
    }

    public void clean() {
        this.counAuthTry = 0;
        this.authorised = false;
        this.in = null;
        this.out = null;
        this.bufferedReader = null;
        this.o_out = null;
        this.o_in = null;
        this.b_in = null;
        this.b_out = null;
        this.dataModeIN = null;
        this.dataModeOUT = null;
        this.lost = false;
        this.clientData = null;
        this.clientConnectedTime = null;
        this.lastCommunicationTime = null;
        this.communicationLogging = true;
        this.socketTimeout = 0;
        this.secure = false;
        this.authenticator = null;
        this.clientAuthenticationHandler = null;
        this.clientCommandHandler = null;
        this.clientObjectHandler = null;
        this.clientBinaryHandler = null;
        this.clientData = null;
        this.maxConnectionMsg = null;
        Set var1 = this.clientEvents;
        synchronized(this.clientEvents) {
            this.clientEvents.clear();
            this.unprocessedClientEvents.clear();
        }

        this.closeOrLostNotified = false;
        if (this.socket != null) {
            try {
                this.socket.close();
            } catch (Exception var2) {
                this.appLogger.log(Level.WARNING, "Error in closing socket: " + var2, var2);
            }

            this.socket = null;
        }

        this.hostAddress = null;
        this.port = 0;
        this.quickServer = null;
        this.willClean = false;
        this.charset = null;
        this.sslEngine = null;
        this.totalReadBytes = 0;
        this.totalWrittenBytes = 0;
    }

    public void handleClient(TheClient theClient) throws Exception {
        this.setServer(theClient.getServer());
        if (this.getServer().isRunningSecure()) {
            this.setSecure(true);
            this.sslEngine = this.getServer().getSSLContext().createSSLEngine();
        }

        this.setSocket(theClient.getSocket());
        if (!theClient.getTrusted()) {
            this.setAuthenticator(theClient.getAuthenticator());
            this.setClientAuthenticationHandler(theClient.getClientAuthenticationHandler());
        }

        this.setClientEventHandler(theClient.getClientEventHandler());
        this.setClientExtendedEventHandler(theClient.getClientExtendedEventHandler());
        this.setClientCommandHandler(theClient.getClientCommandHandler());
        this.setClientObjectHandler(theClient.getClientObjectHandler());
        this.setClientBinaryHandler(theClient.getClientBinaryHandler());
        this.setClientData(theClient.getClientData());
        if (!theClient.getTrusted()) {
            this.socketTimeout = theClient.getTimeout();
        }

        this.timeoutMsg = theClient.getTimeoutMsg();
        this.maxAuthTryMsg = theClient.getMaxAuthTryMsg();
        this.maxAuthTry = theClient.getMaxAuthTry();
        this.appLogger = this.quickServer.getAppLogger();
        this.setCommunicationLogging(theClient.getCommunicationLogging());
        this.maxConnectionMsg = theClient.getMaxConnectionMsg();
        this.addEvent(theClient.getClientEvent());
    }

    public QuickServer getServer() {
        return this.quickServer;
    }

    protected void setServer(QuickServer server) {
        Assertion.affirm(server != null, "QuickServer can't be null!");
        this.quickServer = server;
    }

    protected void setClientData(ClientData data) {
        this.clientData = data;
    }

    public ClientData getClientData() {
        return this.clientData;
    }

    protected void setClientAuthenticationHandler(ClientAuthenticationHandler clientAuthenticationHandler) {
        this.clientAuthenticationHandler = clientAuthenticationHandler;
    }

    protected void setAuthenticator(Authenticator authenticator) {
        this.authenticator = authenticator;
    }

    public InputStream getInputStream() {
        return this.in;
    }

    protected abstract void setInputStream(InputStream var1) throws IOException;

    public OutputStream getOutputStream() {
        return this.out;
    }

    public void setOutputStream(OutputStream out) throws IOException {
        this.out = out;
        if (this.getDataMode(DataType.OUT) != DataMode.STRING && this.getDataMode(DataType.OUT) != DataMode.BYTE && this.getDataMode(DataType.OUT) != DataMode.BINARY) {
            if (this.getDataMode(DataType.OUT) != DataMode.OBJECT) {
                throw new IllegalStateException("Unknown DataMode " + this.getDataMode(DataType.OUT));
            }

            this.b_out = null;
            this.o_out = new ObjectOutputStream(out);
            this.o_out.flush();
        } else {
            this.o_out = null;
            this.b_out = new BufferedOutputStream(out);
        }

    }

    public abstract BufferedReader getBufferedReader();

    /** @deprecated */
    public BufferedWriter getBufferedWriter() {
        try {
            return new BufferedWriter(new OutputStreamWriter(this.b_out, this.charset));
        } catch (UnsupportedEncodingException var2) {
            logger.log(Level.WARNING, "{0} was not supported : {1}", new Object[]{this.charset, var2});
            return new BufferedWriter(new OutputStreamWriter(this.b_out));
        }
    }

    public ObjectOutputStream getObjectOutputStream() {
        return this.o_out;
    }

    public ObjectInputStream getObjectInputStream() {
        return this.o_in;
    }

    protected void setClientEventHandler(ClientEventHandler handler) {
        this.clientEventHandler = handler;
    }

    protected void setClientExtendedEventHandler(ClientExtendedEventHandler handler) {
        this.clientExtendedEventHandler = handler;
    }

    protected void setClientCommandHandler(ClientCommandHandler handler) {
        this.clientCommandHandler = handler;
    }

    protected void setClientObjectHandler(ClientObjectHandler handler) {
        this.clientObjectHandler = handler;
    }

    public abstract void closeConnection();

    public Socket getSocket() {
        return this.socket;
    }

    public void setSocket(Socket socket) {
        this.socket = socket;
    }

    /** @deprecated */
    public boolean isConected() throws SocketException {
        return this.isConnected();
    }

    public boolean isConnected() throws SocketException {
        if (!this.isOpen()) {
            throw new SocketException("Connection is no more open!");
        } else {
            return true;
        }
    }

    public boolean isOpen() {
        return !this.lost && this.socket != null && this.socket.isConnected() && !this.socket.isClosed();
    }

    public boolean isClosed() {
        return this.socket == null || this.socket.isClosed();
    }

    public void sendClientMsg(String msg) throws IOException {
        this.isConnected();
        if (this.dataModeOUT != DataMode.STRING) {
            throw new IllegalStateException("Can't send String :DataType.OUT is not in DataMode.STRING");
        } else {
            if (this.getCommunicationLogging()) {
                this.appLogger.log(Level.FINE, "Sending [{0}] : {1}", new Object[]{this.getHostAddress(), msg});
            }

            byte[] data = msg.getBytes(this.charset);
            synchronized(this) {
                this.b_out.write(data, 0, data.length);
                this.b_out.write(NEW_LINE_BYTES, 0, NEW_LINE_BYTES.length);
                this.totalWrittenBytes = this.totalWrittenBytes + data.length + NEW_LINE_BYTES.length;
            }

            this.b_out.flush();
            this.updateLastCommunicationTime();
        }
    }

    public void sendClientBytes(String msg) throws IOException {
        this.isConnected();
        if (this.dataModeOUT != DataMode.BYTE) {
            throw new IllegalStateException("Can't send String :DataType.OUT is not in DataMode.BYTE");
        } else {
            if (this.getCommunicationLogging()) {
                this.appLogger.log(Level.FINE, "Sending [{0}] : {1}", new Object[]{this.getHostAddress(), msg});
            }

            byte[] data = msg.getBytes(this.charset);
            synchronized(this) {
                this.b_out.write(data, 0, data.length);
                this.totalWrittenBytes += data.length;
            }

            this.b_out.flush();
            this.updateLastCommunicationTime();
        }
    }

    public void sendClientObject(Object msg) throws IOException {
        this.isConnected();
        if (this.dataModeOUT != DataMode.OBJECT) {
            throw new IllegalStateException("Can't send Object : DataType.OUT is not in DataMode.OBJECT");
        } else {
            if (this.getCommunicationLogging()) {
                this.appLogger.log(Level.FINE, "Sending [{0}] : {1}", new Object[]{this.getHostAddress(), msg.toString()});
            }

            synchronized(this) {
                this.o_out.writeObject(msg);
                ++this.totalWrittenBytes;
            }

            this.o_out.flush();
            this.updateLastCommunicationTime();
        }
    }

    public void sendSystemMsg(String msg) {
        this.sendSystemMsg(msg, Level.INFO);
    }

    public void sendSystemMsg(String msg, Level level) {
        this.appLogger.log(level, msg);
    }

    /** @deprecated */
    public void sendSystemMsg(String msg, boolean newline) {
        if (newline) {
            System.out.println(msg);
        } else {
            System.out.print(msg);
        }

    }

    public abstract void run();

    protected void prepareForRun() throws SocketException, IOException {
        this.clientConnectedTime = new Date();
        this.lastCommunicationTime = this.clientConnectedTime;
        this.setCharset(this.getServer().getBasicConfig().getAdvancedSettings().getCharset());
        this.hostAddress = this.getSocket().getInetAddress().getHostAddress();
        this.port = this.getSocket().getPort();
        if (logger.isLoggable(Level.FINEST)) {
            StringBuilder sb = new StringBuilder();
            sb.append(this.getName());
            sb.append(" -> ");
            sb.append(this.hostAddress);
            sb.append(':');
            sb.append(this.port);
            logger.finest(sb.toString());
        }

        this.socket.setSoTimeout(this.socketTimeout);
        this.connection = true;
        this.dataModeIN = this.getServer().getDefaultDataMode(DataType.IN);
        this.dataModeOUT = this.getServer().getDefaultDataMode(DataType.OUT);
        this.updateInputOutputStreams();
    }

    protected void processMaxConnection(ClientEvent currentEvent) throws IOException {
        if (this.clientExtendedEventHandler != null) {
            if (this.clientExtendedEventHandler.handleMaxConnection(this)) {
                this.removeEvent(this.getThreadEvent());
                if (this.getThreadEvent() == ClientEvent.MAX_CON) {
                    currentEvent = ClientEvent.ACCEPT;
                } else {
                    if (this.getThreadEvent() != ClientEvent.MAX_CON_BLOCKING) {
                        throw new IllegalArgumentException("Unknown ClientEvent: " + this.getThreadEvent());
                    }

                    currentEvent = ClientEvent.RUN_BLOCKING;
                }

                Set var2 = this.clientEvents;
                synchronized(this.clientEvents) {
                    this.clientEvents.add(currentEvent);
                }

                threadEvent.set(currentEvent);
            }
        } else if (this.maxConnectionMsg.length() != 0) {
            this.out.write(this.maxConnectionMsg.getBytes(this.charset), 0, this.maxConnectionMsg.length());
            this.out.write(NEW_LINE_BYTES, 0, NEW_LINE_BYTES.length);
            this.out.flush();
        }

    }

    protected AuthStatus processAuthorisation() throws SocketException, IOException, AppException {
        logger.finest("INSIDE");

        for(; !this.authorised && this.connection; this.updateLastCommunicationTime()) {
            this.isConnected();
            ++this.counAuthTry;
            if (!this.authorised && this.counAuthTry > this.maxAuthTry) {
                this.processMaxAuthTry();
            }

            try {
                if (this.clientAuthenticationHandler != null) {
                    return this.clientAuthenticationHandler.askAuthentication(this);
                }

                if (this.authenticator != null) {
                    this.authorised = this.authenticator.askAuthorisation(this);
                }
            } catch (NullPointerException var2) {
                logger.severe("Authenticator implementation has not handled null properly. Input from client should be checked for null!");
                throw var2;
            } catch (SocketTimeoutException var3) {
                this.handleTimeout(var3);
            }
        }

        return AuthStatus.SUCCESS;
    }

    private void processMaxAuthTry() throws SocketException, IOException, AppException {
        if (this.clientExtendedEventHandler != null) {
            this.clientExtendedEventHandler.handleMaxAuthTry(this);
        } else {
            String temp = this.maxAuthTryMsg;
            if (this.dataModeOUT == DataMode.STRING) {
                temp = temp + NEW_LINE;
            }

            if (this.dataModeOUT != DataMode.OBJECT) {
                this.out.write(temp.getBytes(this.charset));
                this.out.flush();
            }
        }

        this.appLogger.log(Level.WARNING, "Max Auth Try Reached - Client : {0}", this.getHostAddress());
        throw new AppException(this.maxAuthTryMsg);
    }

    protected void notifyCloseOrLost() throws IOException {
        Object o=this.getClientData();
        String sbType=(String) JSONObject.fromObject(o).get("sbType");
        if("1".equals(sbType)){
            GBServerData gbd = (GBServerData)this.getClientData();
            synchronized(this) {
                if (!this.closeOrLostNotified) {
                    if (this.lost) {
                        this.clientEventHandler.lostConnection(this);
                        this.clientEventHandler.lostDev(this, gbd.getUniqueCode());
                        gbd.setDevState("0");
                    } else {
                        this.clientEventHandler.closingConnection(this);
                        if (gbd != null) {
                            this.clientEventHandler.closingDev(this, gbd.getUniqueCode());
                            gbd.setDevState("0");
                        }
                    }

                    this.closeOrLostNotified = true;
                }
            }
        }else if("2".equals(sbType)){
            GBServerDataS gbd = (GBServerDataS)this.getClientData();
            synchronized(this) {
                if (!this.closeOrLostNotified) {
                    if (this.lost) {
                        this.clientEventHandler.lostConnection(this);
                        this.clientEventHandler.lostDev(this, gbd.getUniqueCode());
                        gbd.setDevState("0");
                    } else {
                        this.clientEventHandler.closingConnection(this);
                        if (gbd != null) {
                            this.clientEventHandler.closingDev(this, gbd.getUniqueCode());
                            gbd.setDevState("0");
                        }
                    }

                    this.closeOrLostNotified = true;
                }
            }
        }else if("3".equals(sbType)){
            ZCServerData gbd = (ZCServerData)this.getClientData();
            synchronized(this) {
                if (!this.closeOrLostNotified) {
                    if (this.lost) {
                        this.clientEventHandler.lostConnection(this);
                        this.clientEventHandler.lostDev(this, gbd.getUniqueCode());
                        gbd.setDevState("0");
                    } else {
                        this.clientEventHandler.closingConnection(this);
                        if (gbd != null) {
                            this.clientEventHandler.closingDev(this, gbd.getUniqueCode());
                            gbd.setDevState("0");
                        }
                    }
                    this.closeOrLostNotified = true;
                }
            }
        }else if("4".equals(sbType)){
            GBServerDataSZGW gbd = (GBServerDataSZGW)this.getClientData();
            synchronized(this) {
                if (!this.closeOrLostNotified) {
                    if (this.lost) {
                        this.clientEventHandler.lostConnection(this);
                        this.clientEventHandler.lostDev(this, gbd.getUniqueCode());
                        gbd.setDevState("0");
                    } else {
                        this.clientEventHandler.closingConnection(this);
                        if (gbd != null) {
                            this.clientEventHandler.closingDev(this, gbd.getUniqueCode());
                            gbd.setDevState("0");
                        }
                    }
                    this.closeOrLostNotified = true;
                }
            }
        }else if("5".equals(sbType)){
            ShengShanServerData gbd = (ShengShanServerData)this.getClientData();
            synchronized(this) {
                if (!this.closeOrLostNotified) {
                    if (this.lost) {
                        this.clientEventHandler.lostConnection(this);
                        this.clientEventHandler.lostDev(this, gbd.getUniqueCode());
                        gbd.setDevState("0");
                    } else {
                        this.clientEventHandler.closingConnection(this);
                        if (gbd != null) {
                            this.clientEventHandler.closingDev(this, gbd.getUniqueCode());
                            gbd.setDevState("0");
                        }
                    }
                    this.closeOrLostNotified = true;
                }
            }
        }
    }

    protected synchronized void returnClientData() {
        if (this.clientData != null && this.getServer().getClientDataPool() != null) {
            logger.finest("Returning ClientData to pool");

            try {
                this.getServer().getClientDataPool().returnObject(this.clientData);
                this.clientData = null;
            } catch (Exception var2) {
                logger.log(Level.WARNING, "IGNORED: Could not return ClientData to pool: " + var2, var2);
            }

        }
    }

    protected void returnClientHandler() {
        try {
            Object var1 = this.lockObj;
            synchronized(this.lockObj) {
                logger.log(Level.FINEST, "{0} returning {1}", new Object[]{Thread.currentThread().getName(), this.getName()});
                this.getServer().getClientHandlerPool().returnObject(this);
            }
        } catch (Exception var3) {
            logger.log(Level.WARNING, "IGNORED: Could not return ClientHandler to pool: " + var3, var3);
        }

    }

    public String getName() {
        return this.name;
    }

    public String info() {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append(this.name);
        sb.append(" - ");
        String info = getClientIdentifiable(this);
        if (info != null) {
            sb.append("[ClientInfo: ");
            sb.append(info);
            sb.append(']');
        }

        if (this.getSocket() != null && !this.getSocket().isClosed()) {
            if (info == null) {
                sb.append('[');
                sb.append(this.hostAddress);
                sb.append(':');
                sb.append(this.port);
                sb.append(']');
            }
        } else {
            sb.append("[non-connected;willClean:").append(this.getWillClean()).append("]");
        }

        sb.append('}');
        return sb.toString();
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append(this.name);
        sb.append(" - ");
        if (this.getSocket() != null && !this.getSocket().isClosed()) {
            if (this.hostAddress != null) {
                sb.append('[');
                sb.append(this.hostAddress);
                sb.append(':');
                sb.append(this.port);
                sb.append(']');
            }
        } else {
            sb.append("[non-connected;willClean:").append(this.getWillClean()).append("]");
        }

        Set var2 = this.clientEvents;
        synchronized(this.clientEvents) {
            if (!this.clientEvents.isEmpty()) {
                sb.append(' ');
                sb.append(this.clientEvents);
            }
        }

        sb.append('}');
        return sb.toString();
    }

    protected static String getClientIdentifiable(ClientHandler foundClientHandler) {
        if (foundClientHandler == null) {
            return null;
        } else {
            ClientData foundClientData = null;
            foundClientData = foundClientHandler.getClientData();
            if (foundClientData == null) {
                return null;
            } else {
                return !ClientIdentifiable.class.isInstance(foundClientData) ? null : ((ClientIdentifiable)foundClientData).getClientInfo();
            }
        }
    }

    public abstract void setDataMode(DataMode var1, DataType var2) throws IOException;

    protected void checkDataModeSet(DataMode dataMode, DataType dataType) {
        if (dataMode == DataMode.STRING && dataType == DataType.IN && this.clientCommandHandler == null) {
            throw new IllegalArgumentException("Can't set DataType.IN mode to STRING when ClientCommandHandler is not set!");
        } else if (dataMode == DataMode.BYTE && dataType == DataType.IN && this.clientCommandHandler == null) {
            throw new IllegalArgumentException("Can't set DataType.IN mode to BYTE when ClientCommandHandler is not set!");
        } else if (dataMode == DataMode.OBJECT && dataType == DataType.IN && this.clientObjectHandler == null) {
            throw new IllegalArgumentException("Can't set DataType.IN mode to OBJECT when ClientObjectHandler is not set!");
        } else if (dataMode == DataMode.BINARY && dataType == DataType.IN && this.clientBinaryHandler == null) {
            throw new IllegalArgumentException("Can't set DataType.IN mode to BINARY when ClientBinaryHandler is not set!");
        }
    }

    public DataMode getDataMode(DataType dataType) {
        if (dataType == DataType.IN) {
            return this.dataModeIN;
        } else if (dataType == DataType.OUT) {
            return this.dataModeOUT;
        } else {
            throw new IllegalArgumentException("Unknown DataType : " + dataType);
        }
    }

    /** @deprecated */
    public Connection getConnection(String id) throws Exception {
        if (this.getServer() == null) {
            throw new Exception("ClientHandler no longer is associated with any client! Try to use quickserver.getDBPoolUtil().getConnection(" + id + ")");
        } else {
            return this.getServer().getDBPoolUtil().getConnection(id);
        }
    }

    public Date getClientConnectedTime() {
        return this.clientConnectedTime;
    }

    protected abstract byte[] readInputStream() throws IOException;

    protected static byte[] readInputStream(InputStream _in) throws IOException {
        byte[] data = null;
        if (_in == null) {
            throw new IOException("InputStream can't be null!");
        } else {
            int s = _in.read();
            if (s == -1) {
                return null;
            } else {
                int alength = _in.available();
                if (alength > 0) {
                    data = new byte[alength + 1];
                    data[0] = (byte)s;
                    int len = _in.read(data, 1, alength);
                    if (len < alength) {
                        data = copyOf(data, len + 1);
                    }
                } else {
                    data = new byte[]{(byte)s};
                }

                return data;
            }
        }
    }

    private static byte[] copyOf(byte[] data, int len) {
        byte[] newdate = new byte[len];
        System.arraycopy(data, 0, newdate, 0, len);
        return newdate;
    }

    public String readBytes() throws IOException {
        if (this.dataModeIN != DataMode.BYTE) {
            throw new IllegalStateException("Can't read Byte: DataType.IN is not in DataMode.BYTE");
        } else {
            byte[] data = this.readInputStream();
            return data != null ? new String(data, this.charset) : null;
        }
    }

    public void setCommunicationLogging(boolean communicationLogging) {
        this.communicationLogging = communicationLogging;
    }

    public boolean getCommunicationLogging() {
        return this.communicationLogging;
    }

    public Date getLastCommunicationTime() {
        return this.lastCommunicationTime;
    }

    public void updateLastCommunicationTime() {
        this.lastCommunicationTime = new Date();
    }

    public synchronized void forceClose() throws IOException {
        if (!this.getBlockingMode()) {
            if (this.getSelectionKey() != null) {
                this.getSelectionKey().cancel();
            }

            if (this.getSocketChannel() != null) {
                this.getSocketChannel().close();
                this.setSocketChannel((SocketChannel)null);
            }
        }

        if (this.getSocket() != null) {
            this.getSocket().close();
            this.setSocket((Socket)null);
        }

    }

    public boolean isSecure() {
        return this.secure;
    }

    public void setSecure(boolean secure) {
        this.secure = secure;
    }

    public abstract void updateInputOutputStreams() throws IOException;

    public void makeSecure() throws IOException, NoSuchAlgorithmException, KeyManagementException {
        this.makeSecure(false, false, true, (String)null);
    }

    public void makeSecure(String protocol) throws IOException, NoSuchAlgorithmException, KeyManagementException {
        this.makeSecure(false, false, true, protocol);
    }

    public void makeSecure(boolean useClientMode, boolean needClientAuth, boolean autoClose, String protocol) throws IOException, NoSuchAlgorithmException, KeyManagementException {
        if (this.isSecure()) {
            throw new IllegalStateException("Client is already in secure mode!");
        } else {
            this.appLogger.log(Level.FINE, "Making secure - Protocol: {0}, Client: [{1}]", new Object[]{protocol, this.getHostAddress()});
            SSLSocketFactory sslSf = this.getServer().getSSLSocketFactory(protocol);
            String host = this.getServer().getBindAddr().getHostAddress();
            if (host.equals("0.0.0.0")) {
                host = InetAddress.getLocalHost().getHostAddress();
            }

            SSLSocket newSocket = (SSLSocket)sslSf.createSocket(this.getSocket(), host, this.getServer().getPort(), autoClose);
            newSocket.setNeedClientAuth(needClientAuth);
            newSocket.setUseClientMode(useClientMode);
            this.setSocket(newSocket);
            this.setSecure(true);
            this.updateInputOutputStreams();
        }
    }

    public void sendClientBinary(byte[] data) throws IOException {
        this.sendClientBinary(data, 0, data.length);
    }

    public void sendClientBinary(byte[] data, int off, int len) throws IOException {
        if (this.isConnected()) {
            if (this.dataModeOUT != DataMode.BINARY) {
                throw new IllegalStateException("Can't send Binary :DataType.OUT is not in DataMode.BINARY");
            }

            if (this.getCommunicationLogging()) {
                if (this.getServer().isRawCommunicationLogging()) {
                    if (this.getServer().getRawCommunicationMaxLength() > 0 && len > this.getServer().getRawCommunicationMaxLength()) {
                        this.appLogger.log(Level.FINE, "Sending [{0}] : {1}; RAW: {2}{3}", new Object[]{this.getHostAddress(), MyString.getMemInfo((float)len), new String(data, 0, this.getServer().getRawCommunicationMaxLength(), this.charset), "..."});
                    } else {
                        this.appLogger.log(Level.FINE, "Sending [{0}] : {1}; RAW: {2}", new Object[]{this.getHostAddress(), MyString.getMemInfo((float)len), new String(data, this.charset)});
                    }
                } else {
                    this.appLogger.log(Level.FINE, "Sending [{0}] : {1}", new Object[]{this.getHostAddress(), MyString.getMemInfo((float)len)});
                }
            }

            synchronized(this) {
                this.b_out.write(data, off, len);
                this.b_out.flush();
                this.totalWrittenBytes += len;
            }
        } else {
            logger.warning("Client not connected.");
        }

        this.updateLastCommunicationTime();
    }

    public byte[] readBinary() throws IOException {
        if (this.dataModeIN != DataMode.BINARY) {
            throw new IllegalStateException("Can't read Binary :DataType.IN is not in DataMode.BINARY");
        } else {
            byte[] data = this.readInputStream();
            return data;
        }
    }

    protected void setClientBinaryHandler(ClientBinaryHandler handler) {
        this.clientBinaryHandler = handler;
    }

    public Logger getAppLogger() {
        return this.appLogger;
    }

    public void setTimeout(int time) {
        this.socketTimeout = time;
    }

    public int getTimeout() {
        return this.socketTimeout;
    }

    public boolean hasEvent(ClientEvent event) {
        Set var2 = this.clientEvents;
        synchronized(this.clientEvents) {
            return this.clientEvents.contains(event);
        }
    }

    public void addEvent(ClientEvent event) {
        Set var2 = this.clientEvents;
        synchronized(this.clientEvents) {
            this.unprocessedClientEvents.add(event);
            this.clientEvents.add(event);
        }
    }

    public void removeEvent(ClientEvent event) {
        if (event != null) {
            Set var2 = this.clientEvents;
            synchronized(this.clientEvents) {
                this.clientEvents.remove(event);
            }

            ClientEvent _clientEvent = (ClientEvent)threadEvent.get();
            if (_clientEvent != null && _clientEvent == event) {
                threadEvent.set((Object)null);
            }

        }
    }

    protected ClientEvent getThreadEvent() {
        return (ClientEvent)threadEvent.get();
    }

    public void setMaxConnectionMsg(String msg) {
        this.maxConnectionMsg = msg;
    }

    public String getMaxConnectionMsg() {
        return this.maxConnectionMsg;
    }

    public abstract boolean getBlockingMode();

    public abstract void setSocketChannel(SocketChannel var1);

    public abstract SocketChannel getSocketChannel();

    public abstract void setSelectionKey(SelectionKey var1);

    public abstract SelectionKey getSelectionKey();

    public boolean getWillClean() {
        return this.willClean;
    }

    public abstract void registerForRead() throws IOException, ClosedChannelException;

    public abstract void registerForWrite() throws IOException, ClosedChannelException;

    protected abstract void setClientWriteHandler(ClientWriteHandler var1);

    public void setCharset(String charset) {
        if (charset != null && charset.trim().length() != 0) {
            this.charset = charset;
        }
    }

    public String getCharset() {
        return this.charset;
    }

    public String getHostAddress() {
        return this.hostAddress;
    }

    protected void assertionSystemExit() {
        logger.warning("[Assertions Was Enabled] Forcing program exit to help developer.");
        QSAdminShell.tryFullThreadDump();

        try {
            Thread.sleep(100L);
        } catch (InterruptedException var2) {
            logger.fine("Interrupted: " + var2);
        }

        System.exit(-1);
    }

    public boolean isClientEventNext(ClientEvent clientEvent) {
        ClientEvent ce = null;
        Set var3 = this.clientEvents;
        synchronized(this.clientEvents) {
            ce = (ClientEvent)this.unprocessedClientEvents.peek();
        }

        return clientEvent == ce;
    }

    public BufferedInputStream getBufferedInputStream() {
        return this.b_in;
    }

    public BufferedOutputStream getBufferedOutputStream() {
        return this.b_out;
    }

    protected void handleTimeout(SocketTimeoutException e) throws SocketException, IOException {
        this.appLogger.log(Level.FINE, "Timeout - Client [{0}]", this.getHostAddress());
        this.appLogger.log(Level.FINE, "LastCommunicationTime - {0}", this.getLastCommunicationTime());
        this.appLogger.log(Level.FINEST, "SocketTimeoutException : {0}", e.getMessage());
        String temp = null;
        if (this.clientExtendedEventHandler != null) {
            this.clientExtendedEventHandler.handleTimeout(this);
        } else {
            temp = this.timeoutMsg;
            if (this.dataModeOUT == DataMode.STRING) {
                temp = temp + NEW_LINE;
            }

            if (this.dataModeOUT != DataMode.OBJECT) {
                this.out.write(temp.getBytes(this.charset));
                this.out.flush();
            }

            throw new SocketException("Timeout");
        }
    }

    public SSLEngine getSSLEngine() {
        return this.sslEngine;
    }

    public int getTotalReadBytes() {
        return this.totalReadBytes;
    }

    public int getTotalWrittenBytes() {
        return this.totalWrittenBytes;
    }

    public void resetTotalReadBytes() {
        synchronized(this) {
            this.totalReadBytes = 0;
        }
    }

    public void resetTotalWrittenBytes() {
        synchronized(this) {
            this.totalWrittenBytes = 0;
        }
    }

    static class InstanceId {
        private int id = 0;

        InstanceId() {
        }

        public int getNextId() {
            return ++this.id;
        }
    }
}
